package axes;

require Exporter;

our @ISA = qw(Exporter);
our $VERSION = 1.00;
our @EXPORT = qw(nice_intervals nice_num );

use strict;
use warnings;
use IO::File;
use POSIX qw(floor ceil log10);
use List::Util qw(min max);

# function declarations
sub expt($$)
{
	my ($a, $n) = @_;
    my $x = 1.0;
	$n = int($n);
    if ($n > 0)
	{
		$x *= $a	while ($n-- > 0);
	}
    else
	{
		$x /= $a	while ($n++ < 0);
	}
    return $x;
}

sub nice_intervals($$$)
{
	my ($min, $max, $max_cnt_interval) = @_;
	my ($new_cnt_interval);
	my $cnt_interval = $max_cnt_interval;
	my $range   ;
	my $interval;
	my $graphmin;
	my $graphmax;

    # we expect min!=max
	while (1)
	{
		$range		= nice_num($max - $min, 0);
		$interval	= nice_num($range / ($cnt_interval ), 1);
		$graphmin	= int(floor($min / $interval)) * $interval;
		$graphmax	= int (ceil ($max / $interval)) * $interval;
		$new_cnt_interval = int(($graphmax - $graphmin)/ $interval + 0.5);
		last if ($new_cnt_interval <= $max_cnt_interval);
		$cnt_interval = ($cnt_interval + 1 ) / 2;
#		$cnt_interval--;
		print STDERR '.';
	}

	return ($new_cnt_interval, $interval, min($graphmin, $graphmax), max($graphmin, $graphmax));
}

sub nice_num($$)
{
	my ($x, $round) = @_;

	# exponent of $x
    my $expv = floor( log10( $x ));

	# fractional part of $x
	# between 1 and 10
	my $const	= expt(10.0, $expv);
    my $f		= $x / $const;
    if ($round)
	{
		return $const if ($f < 1.5);
		return $const * 2 if ($f < 3.);
		return $const * 5 if ($f < 7.);
        return $const * 10;
	}
	else
	{
		return $const if ($f <= 1.);
		return $const * 2 if ($f <= 2.);
		return $const * 5 if ($f <= 5.);
		return $const * 10;
	}
}


__DATA__
my $sql =<<'cpp';
double nice_num();

/* expt(a,n)=a^n for integer n */

#ifdef POW_NOT_TRUSTWORTHY
/* if roundoff errors in pow cause problems, use this: */

double expt(double a, int n)
{
    double x;

    x = 1.;
    if (n>0) for (; n>0; n--) x *= a;
    else for (; n<0; n++) x /= a;
    return x;
}

#else
#   define expt(a, n) pow(a, (double)(n))
#endif

#define NTICK 5			/* desired number of tick marks */

/*
 * loose_label: demonstrate loose labeling of data range from min to max.
 * (tight method is similar)
 */

void loose_label(double min, max)
{
    char str[6], temp[20];
    int nfrac;
    double d;				/* tick mark spacing */
    double graphmin, graphmax;		/* graph range min and max */
    double range, x;

    /* we expect min!=max */
    range = nice_num(max-min, 0);
    d = nice_num(range/(NTICK-1), 1);
    graphmin = floor(min/d)*d;
    graphmax = ceil(max/d)*d;
    nfrac = MAX(-floor(log10(d)), 0);	/* # of fractional digits to show */
    sprintf(str, "%%.%df", nfrac);	/* simplest axis labels */

    printf("graphmin=%g graphmax=%g increment=%g\n", graphmin, graphmax, d);
    for (x=graphmin; x<graphmax+.5*d; x+=d) {
	sprintf(temp, str, x);
	printf("(%s)\n", temp);
    }
}

/*
 * nice_num: find a "nice" number approximately equal to x.
 * Round the number if round=1, take ceiling if round=0
 */

double nice_num(double x, int round)
{
    double f;				
    double nf;				/* nice, rounded fraction */

	/* exponent of x */
    int expv = floor(log10(x));
	/* fractional part of x */
	/* between 1 and 10 */
    double f = x/expt(10., expv);		
    if (round)
		if (f<1.5)
			nf = 1.;
		else if (f<3.)
			nf = 2.;
		else if (f<7.)
			nf = 5.;
		else
			nf = 10.;
	else
		if (f<=1.)
			nf = 1.;
		else if (f<=2.)
			nf = 2.;
		else if (f<=5.)
			nf = 5.;
		else
		nf = 10.;
    return nf*expt(10., expv);
}
cpp
